Tumor evolution project

Data used

In this notebook, we are using the tmb_genomic.tsv file generated from the 01-preprocess-data.Rmd script.

Set up

suppressPackageStartupMessages({
  library(tidyverse)
})

Directories and File Inputs/Outputs

# Detect the ".git" folder. This will be in the project root directory.
# Use this as the root directory to ensure proper sourcing of functions
# no matter where this is called from.
root_dir <- rprojroot::find_root(rprojroot::has_dir(".git"))
scratch_dir <- file.path(root_dir, "scratch")
analysis_dir <- file.path(root_dir, "analyses", "tmb-vaf-longitudinal") 
input_dir <- file.path(analysis_dir, "input")

# Input files
tmb_genomic_file <- file.path(scratch_dir, "tmb_genomic.tsv")
palette_file <- file.path(root_dir, "figures", "palettes", "tumor_descriptor_color_palette.tsv")

# File path to plots directory
plots_dir <-
  file.path(analysis_dir, "plots")
if (!dir.exists(plots_dir)) {
  dir.create(plots_dir)
}

source(paste0(analysis_dir, "/util/function-create-barplot.R"))
source(paste0(analysis_dir, "/util/function-create-dumbbell-plot.R"))
source(paste0(root_dir, "/figures/scripts/theme.R"))

Read in data and process

# Read and process tmb_genomic file
df_total <- readr::read_tsv(tmb_genomic_file, guess_max = 100000, show_col_types = FALSE) %>% 
  group_by(Kids_First_Participant_ID) %>% 
  mutate(cg_distinct = n_distinct(cancer_group) > 1) # to identify samples with different diagnosis across timepoints

# Are there any samples with both WGS and WXS? 
df_total %>% 
  unique() %>% 
  arrange(Kids_First_Participant_ID, experimental_strategy) %>%
  group_by(Kids_First_Participant_ID) %>%
  dplyr::summarise(experimental_strategy_sum = str_c(experimental_strategy, collapse = ";")) 
# There are, so let's remove these from downstream analyses.
df <- df_total %>% 
  filter(!experimental_strategy == "WXS") %>% 
  dplyr::mutate(patient_id = paste(short_histology, Kids_First_Participant_ID, sep = "_")) %>% 
  distinct(cancer_group, .keep_all = TRUE) %>% 
  summarise(cg_sum = str_c(cancer_group, collapse = ",")) %>% # to identify cases with multiple diagnosis
  left_join(df_total, by = c("Kids_First_Participant_ID")) %>% 
  select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, cg_sum, cancer_group, short_histology, tumor_descriptor, descriptors, tumor_descriptor_sum, tmb, mutation_count)

# How many bs_samples per cg_sum?
print(df %>% count(cg_sum) %>% arrange(desc(n))) 
# A tibble: 37 × 2
   cg_sum                                 n
   <chr>                              <int>
 1 Diffuse midline glioma                52
 2 Ependymoma                            45
 3 Low-grade glioma                      34
 4 High-grade glioma                     28
 5 Medulloblastoma                       26
 6 Atypical Teratoid Rhabdoid Tumor      25
 7 Chordoma                              12
 8 Meningioma                            10
 9 Ewing sarcoma                          7
10 Adamantinomatous Craniopharyngioma     6
# ℹ 27 more rows
# Let's summarize cancer groups with < 10 bs_samples as Other and use this for visualization purposes
cg_sum_df <- df %>% 
  count(cg_sum) %>% 
  dplyr::mutate(cg_sum_n = glue::glue("{cg_sum} (N={n})"))

df <- df %>% 
  left_join(cg_sum_df, by = c("cg_sum")) %>% 
  mutate(cg_plot = case_when(n < 10 ~ "Other",
                             TRUE ~ cg_sum))

# How many bs_samples per cg_plot?
print(df %>% count(cg_plot) %>% arrange(desc(n))) 
# A tibble: 9 × 2
  cg_plot                              n
  <chr>                            <int>
1 Other                               89
2 Diffuse midline glioma              52
3 Ependymoma                          45
4 Low-grade glioma                    34
5 High-grade glioma                   28
6 Medulloblastoma                     26
7 Atypical Teratoid Rhabdoid Tumor    25
8 Chordoma                            12
9 Meningioma                          10
# Read color palette
palette_df <- readr::read_tsv(palette_file, guess_max = 100000, show_col_types = FALSE)

# length(unique(df$Kids_First_Participant_ID))

TMB per Patient case

We will explore TMB per Kids_First_Participant_ID over time by creating stacked barplots.

# Define parameters for function
ylim = max(df$tmb)

# Re-order df
f <- c("Second Malignancy", "Unavailable", "Deceased", "Recurrence", "Progressive", "Diagnosis") # Level df by timepoints
df_plot <- df %>% 
  dplyr:::mutate(tumor_descriptor = factor(tumor_descriptor),
                 tumor_descriptor = fct_relevel(tumor_descriptor, f)) 

# Run function
fname <- paste0(plots_dir, "/", "TMB-genomic-total.pdf")
print(fname)
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/TMB-genomic-total.pdf"
p <- create_stacked_barplot(tmb_df = df_plot, ylim = ylim)
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Warning: Removed 1 rows containing missing values (`geom_col()`).
Warning: Removed 1 rows containing missing values (`geom_bar()`).

pdf(file = fname, width = 15, height = 6)
print(p)
Warning: Removed 1 rows containing missing values (`geom_col()`).
Removed 1 rows containing missing values (`geom_bar()`).
dev.off()
png 
  2 

Attention: Hypermutant TMB defined as ≥10 Mb, and Ultrahypermutant TMB defined as ≥100 mutations/Mb in pediatric brain tumors (https://pubmed.ncbi.nlm.nih.gov/29056344/).

Here, we notice that there are samples with high TMB (hyper-mutant samples). Next, we will exclude these samples (threshold >= 10) from downstream analysis. Attention is needed in cases with high number of mutations in only one timepoint as this will lead to un-matched longitudinal samples. We will also remove those so we always have matched longitudinal samples.

# Filter df and remove any samples with single timepoints
df_plot_filter <- df %>%
  filter(!tmb >= 10) %>%
  unique() %>% 
  arrange(Kids_First_Participant_ID, tumor_descriptor) %>%
  group_by(Kids_First_Participant_ID) %>%
  dplyr::summarise(tumor_descriptor_sum = str_c(tumor_descriptor, collapse = ";")) %>% 
  filter(!tumor_descriptor_sum %in% c("Diagnosis", "Progressive", "Recurrence", "Second Malignancy", "Unavailable", "Deceased", "Progressive;Progressive")) %>% 
  dplyr::left_join(df, by = c("Kids_First_Participant_ID", "tumor_descriptor_sum")) %>% 
  mutate(tumor_descriptor = factor(tumor_descriptor),
         tumor_descriptor = fct_relevel(tumor_descriptor, f)) %>% 
  drop_na(tmb) 

# length(unique(df_plot_filter$Kids_First_Participant_ID))

# Define parameters for function
ylim = max(df_plot_filter$tmb)
df_plot_filter <- df_plot_filter

# Run function
fname <- paste0(plots_dir, "/", "TMB-genomic-no-hypermutants.pdf")
print(fname)
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/TMB-genomic-no-hypermutants.pdf"
p <- create_stacked_barplot(tmb_df = df_plot_filter, ylim = ylim)
Warning: Removed 1 rows containing missing values (`geom_col()`).
Warning: Removed 1 rows containing missing values (`geom_bar()`).

pdf(file = fname, width = 25, height = 8)
print(p)
Warning: Removed 1 rows containing missing values (`geom_col()`).
Removed 1 rows containing missing values (`geom_bar()`).
dev.off()
png 
  2 

TMB across timepoints and cancer types per Patient case

We will explore TMB per cancer group over time by creating dumbbell plots. We classified by using cancer types with the highest number of samples (High- and Low-grade gliomas) versus any other cancer groups.

# How many bs_samples per kids_id and cancer group?
# print(table(df_plot_filter$cg_plot))
print(df_plot_filter %>% 
        count(cg_plot, Kids_First_Participant_ID))
# A tibble: 51 × 3
   cg_plot                          Kids_First_Participant_ID     n
   <chr>                            <chr>                     <int>
 1 Atypical Teratoid Rhabdoid Tumor PT_DVXE38EX                   4
 2 Atypical Teratoid Rhabdoid Tumor PT_HE8FBFNA                   2
 3 Atypical Teratoid Rhabdoid Tumor PT_VTG1S395                   2
 4 Diffuse midline glioma           PT_5NS35B66                   2
 5 Diffuse midline glioma           PT_JNEV57VK                   2
 6 Diffuse midline glioma           PT_JSFBMK5V                   2
 7 Diffuse midline glioma           PT_NK8A49X5                   5
 8 Diffuse midline glioma           PT_P571HTNK                   2
 9 Diffuse midline glioma           PT_PR4YBBH3                   2
10 Ependymoma                       PT_3R0P995B                   2
# ℹ 41 more rows
# Dumbbell plot per cancer group
cancer_groups <- unique(as.character(df_plot_filter$cg_plot))
cancer_groups <- sort(cancer_groups, decreasing = FALSE)
print(cancer_groups)
[1] "Atypical Teratoid Rhabdoid Tumor" "Diffuse midline glioma"          
[3] "Ependymoma"                       "High-grade glioma"               
[5] "Low-grade glioma"                 "Medulloblastoma"                 
[7] "Meningioma"                       "Other"                           
for (i in seq_along(cancer_groups)) {
  print(i)
  df_ct_sub <- df_plot_filter %>% 
    filter(cg_plot == cancer_groups [i])
  
      if (i %in% c(3, 7, 8)) {
    print(cancer_groups [i])
    # Define parameters for function
    ylim <- 2
    } else if (i == 2) {
      print(cancer_groups [i])
      # Define parameters for function
      ylim <- 6
      } else {
        print(cancer_groups [i])
        # Define parameters for function
        ylim <- 4
      }
    

    # Name plots
    fname <- paste0(plots_dir, "/", cancer_groups[i], "-TMB-dumbbell", ".pdf")
    print(fname)
    
    # Run function
    p <- create_dumbbell_ct(tmb_df = df_ct_sub, 
                                 ylim = ylim, 
                                 ct_id = cancer_groups[i])
    pdf(file = fname, width = 12, height = 8)
    print(p)
    dev.off()
}
[1] 1
[1] "Atypical Teratoid Rhabdoid Tumor"
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/Atypical Teratoid Rhabdoid Tumor-TMB-dumbbell.pdf"

[1] 2
[1] "Diffuse midline glioma"
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/Diffuse midline glioma-TMB-dumbbell.pdf"
Warning: Removed 1 row containing missing values (`geom_line()`).
Warning: Removed 1 rows containing missing values (`geom_point()`).
Warning: Removed 1 row containing missing values (`geom_line()`).
Warning: Removed 1 rows containing missing values (`geom_point()`).

[1] 3
[1] "Ependymoma"
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/Ependymoma-TMB-dumbbell.pdf"

[1] 4
[1] "High-grade glioma"
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/High-grade glioma-TMB-dumbbell.pdf"

[1] 5
[1] "Low-grade glioma"
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/Low-grade glioma-TMB-dumbbell.pdf"

[1] 6
[1] "Medulloblastoma"
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/Medulloblastoma-TMB-dumbbell.pdf"

[1] 7
[1] "Meningioma"
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/Meningioma-TMB-dumbbell.pdf"

[1] 8
[1] "Other"
[1] "/home/rstudio/pbta-tumor-evolution/analyses/tmb-vaf-longitudinal/plots/Other-TMB-dumbbell.pdf"

Total number of mutations across timepoints and biospecimen sample per Patient case

Here, we want to explore the number of mutations per timepoint and biospecimen sample per patient case.

samples <- unique(as.character(df_plot_filter$Kids_First_Participant_ID))
print(samples)
 [1] "PT_02J5CWN5" "PT_0DWRY9ZX" "PT_1ZAWNGWT" "PT_2FVTD0WR" "PT_2MZPGZN1"
 [6] "PT_2YT37G8P" "PT_37B5JRP1" "PT_39H4JN6H" "PT_3GYW6P6P" "PT_3P3HARZ2"
[11] "PT_3R0P995B" "PT_3T3VGWC6" "PT_3VCS1PPF" "PT_5NS35B66" "PT_5ZPPR06P"
[16] "PT_62G82T6Q" "PT_82MX6J77" "PT_89XRZBSG" "PT_962TCBVR" "PT_98QMQZY7"
[21] "PT_99S5BPE3" "PT_B5DQ8FF0" "PT_BRVGRXQY" "PT_BZCJMEX8" "PT_CWXSP19D"
[26] "PT_CXT81GRM" "PT_DFQAH7RS" "PT_DNAJYFZT" "PT_DVXE38EX" "PT_FN4GEEFR"
[31] "PT_HE8FBFNA" "PT_HHG37M6W" "PT_JNEV57VK" "PT_JSFBMK5V" "PT_KMHGNCNR"
[36] "PT_NK8A49X5" "PT_P571HTNK" "PT_PF04R0BH" "PT_PR4YBBH3" "PT_QH9H491G"
[41] "PT_S2SQJVGK" "PT_T4VN7ZRB" "PT_TKWTTRQ7" "PT_TP6GS00H" "PT_TRZ1N1HQ"
[46] "PT_VTG1S395" "PT_XA98HG1C" "PT_XHYBZKCX" "PT_YK7AD0KK" "PT_Z4GS3ZQQ"
[51] "PT_ZMKMKCFQ"
for (i in seq_along(samples)) {
  print(i)
  tmb_sub <- df_plot_filter %>%
    filter(Kids_First_Participant_ID == samples[i])
  
  # Define parameters for function
  ylim = max(df_plot_filter$mutation_count)
 
  # Run function
  p <- create_barplot_sample(tmb_df = tmb_sub,
                             ylim = ylim,
                             sid = samples[i])
  print(p)
}
[1] 1

[1] 2

[1] 3

[1] 4

[1] 5

[1] 6

[1] 7

[1] 8

[1] 9

[1] 10

[1] 11

[1] 12

[1] 13

[1] 14

[1] 15

[1] 16

[1] 17

[1] 18

[1] 19

[1] 20

[1] 21

[1] 22

[1] 23

[1] 24

[1] 25

[1] 26

[1] 27

[1] 28

[1] 29

[1] 30

[1] 31

[1] 32

[1] 33

[1] 34

[1] 35

[1] 36

[1] 37

[1] 38

[1] 39

[1] 40

[1] 41

[1] 42

[1] 43

[1] 44

[1] 45

[1] 46

[1] 47

[1] 48

[1] 49

[1] 50

[1] 51

sessionInfo()
R version 4.2.3 (2023-03-15)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 22.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] grid      stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
 [1] ggthemes_4.2.4  lubridate_1.9.2 forcats_1.0.0   stringr_1.5.0  
 [5] dplyr_1.1.1     purrr_1.0.1     readr_2.1.4     tidyr_1.3.0    
 [9] tibble_3.2.1    ggplot2_3.4.0   tidyverse_2.0.0

loaded via a namespace (and not attached):
 [1] highr_0.10       bslib_0.4.2      compiler_4.2.3   pillar_1.9.0    
 [5] jquerylib_0.1.4  tools_4.2.3      bit_4.0.5        digest_0.6.31   
 [9] timechange_0.2.0 jsonlite_1.8.4   evaluate_0.20    lifecycle_1.0.3 
[13] gtable_0.3.3     pkgconfig_2.0.3  rlang_1.1.0      cli_3.6.1       
[17] parallel_4.2.3   yaml_2.3.7       xfun_0.38        fastmap_1.1.1   
[21] withr_2.5.0      knitr_1.42       generics_0.1.3   vctrs_0.6.2     
[25] sass_0.4.5       hms_1.1.3        bit64_4.0.5      rprojroot_2.0.3 
[29] tidyselect_1.2.0 glue_1.6.2       R6_2.5.1         fansi_1.0.4     
[33] vroom_1.6.1      rmarkdown_2.21   farver_2.1.1     tzdb_0.3.0      
[37] magrittr_2.0.3   scales_1.2.1     htmltools_0.5.5  colorspace_2.1-0
[41] labeling_0.4.2   utf8_1.2.3       stringi_1.7.12   munsell_0.5.0   
[45] cachem_1.0.7     crayon_1.5.2    
LS0tCnRpdGxlOiAiRXhwbG9yZSBUTUIgYW5kIG51bWJlciBvZiBtdXRhdGlvbnMgYWNyb3NzIG11bHRpcGxlIHRpbWVwb2ludHMgb2YgdGhlIFBCVEEgQ29ob3J0IgphdXRob3I6ICJBbnRvbmlhIENocm9uaSA8Y2hyb25pYUBjaG9wLmVkdT4gZm9yIEQzQiIKZGF0ZTogIjIwMjMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKLS0tCgojIyMjIFR1bW9yIGV2b2x1dGlvbiBwcm9qZWN0IAoKIyMjIERhdGEgdXNlZCAKSW4gdGhpcyBub3RlYm9vaywgd2UgYXJlIHVzaW5nIHRoZSBgdG1iX2dlbm9taWMudHN2YCBmaWxlIGdlbmVyYXRlZCBmcm9tIHRoZSBgMDEtcHJlcHJvY2Vzcy1kYXRhLlJtZGAgc2NyaXB0LgoKIyBTZXQgdXAKYGBge3IgbG9hZC1saWJyYXJ5fQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkodGlkeXZlcnNlKQp9KQpgYGAKCiMgRGlyZWN0b3JpZXMgYW5kIEZpbGUgSW5wdXRzL091dHB1dHMKYGBge3Igc2V0LWRpci1hbmQtZmlsZS1uYW1lc30KIyBEZXRlY3QgdGhlICIuZ2l0IiBmb2xkZXIuIFRoaXMgd2lsbCBiZSBpbiB0aGUgcHJvamVjdCByb290IGRpcmVjdG9yeS4KIyBVc2UgdGhpcyBhcyB0aGUgcm9vdCBkaXJlY3RvcnkgdG8gZW5zdXJlIHByb3BlciBzb3VyY2luZyBvZiBmdW5jdGlvbnMKIyBubyBtYXR0ZXIgd2hlcmUgdGhpcyBpcyBjYWxsZWQgZnJvbS4Kcm9vdF9kaXIgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290OjpoYXNfZGlyKCIuZ2l0IikpCnNjcmF0Y2hfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwgInNjcmF0Y2giKQphbmFseXNpc19kaXIgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLCAiYW5hbHlzZXMiLCAidG1iLXZhZi1sb25naXR1ZGluYWwiKSAKaW5wdXRfZGlyIDwtIGZpbGUucGF0aChhbmFseXNpc19kaXIsICJpbnB1dCIpCgojIElucHV0IGZpbGVzCnRtYl9nZW5vbWljX2ZpbGUgPC0gZmlsZS5wYXRoKHNjcmF0Y2hfZGlyLCAidG1iX2dlbm9taWMudHN2IikKcGFsZXR0ZV9maWxlIDwtIGZpbGUucGF0aChyb290X2RpciwgImZpZ3VyZXMiLCAicGFsZXR0ZXMiLCAidHVtb3JfZGVzY3JpcHRvcl9jb2xvcl9wYWxldHRlLnRzdiIpCgojIEZpbGUgcGF0aCB0byBwbG90cyBkaXJlY3RvcnkKcGxvdHNfZGlyIDwtCiAgZmlsZS5wYXRoKGFuYWx5c2lzX2RpciwgInBsb3RzIikKaWYgKCFkaXIuZXhpc3RzKHBsb3RzX2RpcikpIHsKICBkaXIuY3JlYXRlKHBsb3RzX2RpcikKfQoKc291cmNlKHBhc3RlMChhbmFseXNpc19kaXIsICIvdXRpbC9mdW5jdGlvbi1jcmVhdGUtYmFycGxvdC5SIikpCnNvdXJjZShwYXN0ZTAoYW5hbHlzaXNfZGlyLCAiL3V0aWwvZnVuY3Rpb24tY3JlYXRlLWR1bWJiZWxsLXBsb3QuUiIpKQpzb3VyY2UocGFzdGUwKHJvb3RfZGlyLCAiL2ZpZ3VyZXMvc2NyaXB0cy90aGVtZS5SIikpCmBgYAoKIyBSZWFkIGluIGRhdGEgYW5kIHByb2Nlc3MKYGBge3IgcmVhZF9pbnB1dF9maWxlc30KIyBSZWFkIGFuZCBwcm9jZXNzIHRtYl9nZW5vbWljIGZpbGUKZGZfdG90YWwgPC0gcmVhZHI6OnJlYWRfdHN2KHRtYl9nZW5vbWljX2ZpbGUsIGd1ZXNzX21heCA9IDEwMDAwMCwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lIAogIGdyb3VwX2J5KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpICU+JSAKICBtdXRhdGUoY2dfZGlzdGluY3QgPSBuX2Rpc3RpbmN0KGNhbmNlcl9ncm91cCkgPiAxKSAjIHRvIGlkZW50aWZ5IHNhbXBsZXMgd2l0aCBkaWZmZXJlbnQgZGlhZ25vc2lzIGFjcm9zcyB0aW1lcG9pbnRzCgojIEFyZSB0aGVyZSBhbnkgc2FtcGxlcyB3aXRoIGJvdGggV0dTIGFuZCBXWFM/IApkZl90b3RhbCAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGFycmFuZ2UoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgZXhwZXJpbWVudGFsX3N0cmF0ZWd5KSAlPiUKICBncm91cF9ieShLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGV4cGVyaW1lbnRhbF9zdHJhdGVneV9zdW0gPSBzdHJfYyhleHBlcmltZW50YWxfc3RyYXRlZ3ksIGNvbGxhcHNlID0gIjsiKSkgCgojIFRoZXJlIGFyZSwgc28gbGV0J3MgcmVtb3ZlIHRoZXNlIGZyb20gZG93bnN0cmVhbSBhbmFseXNlcy4KZGYgPC0gZGZfdG90YWwgJT4lIAogIGZpbHRlcighZXhwZXJpbWVudGFsX3N0cmF0ZWd5ID09ICJXWFMiKSAlPiUgCiAgZHBseXI6Om11dGF0ZShwYXRpZW50X2lkID0gcGFzdGUoc2hvcnRfaGlzdG9sb2d5LCBLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCBzZXAgPSAiXyIpKSAlPiUgCiAgZGlzdGluY3QoY2FuY2VyX2dyb3VwLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUgCiAgc3VtbWFyaXNlKGNnX3N1bSA9IHN0cl9jKGNhbmNlcl9ncm91cCwgY29sbGFwc2UgPSAiLCIpKSAlPiUgIyB0byBpZGVudGlmeSBjYXNlcyB3aXRoIG11bHRpcGxlIGRpYWdub3NpcwogIGxlZnRfam9pbihkZl90b3RhbCwgYnkgPSBjKCJLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEIikpICU+JSAKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgY2dfc3VtLCBjYW5jZXJfZ3JvdXAsIHNob3J0X2hpc3RvbG9neSwgdHVtb3JfZGVzY3JpcHRvciwgZGVzY3JpcHRvcnMsIHR1bW9yX2Rlc2NyaXB0b3Jfc3VtLCB0bWIsIG11dGF0aW9uX2NvdW50KQoKIyBIb3cgbWFueSBic19zYW1wbGVzIHBlciBjZ19zdW0/CnByaW50KGRmICU+JSBjb3VudChjZ19zdW0pICU+JSBhcnJhbmdlKGRlc2MobikpKSAKCiMgTGV0J3Mgc3VtbWFyaXplIGNhbmNlciBncm91cHMgd2l0aCA8IDEwIGJzX3NhbXBsZXMgYXMgT3RoZXIgYW5kIHVzZSB0aGlzIGZvciB2aXN1YWxpemF0aW9uIHB1cnBvc2VzCmNnX3N1bV9kZiA8LSBkZiAlPiUgCiAgY291bnQoY2dfc3VtKSAlPiUgCiAgZHBseXI6Om11dGF0ZShjZ19zdW1fbiA9IGdsdWU6OmdsdWUoIntjZ19zdW19IChOPXtufSkiKSkKCmRmIDwtIGRmICU+JSAKICBsZWZ0X2pvaW4oY2dfc3VtX2RmLCBieSA9IGMoImNnX3N1bSIpKSAlPiUgCiAgbXV0YXRlKGNnX3Bsb3QgPSBjYXNlX3doZW4obiA8IDEwIH4gIk90aGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gY2dfc3VtKSkKCiMgSG93IG1hbnkgYnNfc2FtcGxlcyBwZXIgY2dfcGxvdD8KcHJpbnQoZGYgJT4lIGNvdW50KGNnX3Bsb3QpICU+JSBhcnJhbmdlKGRlc2MobikpKSAKCiMgUmVhZCBjb2xvciBwYWxldHRlCnBhbGV0dGVfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHBhbGV0dGVfZmlsZSwgZ3Vlc3NfbWF4ID0gMTAwMDAwLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKIyBsZW5ndGgodW5pcXVlKGRmJEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpKQpgYGAKCiMgVE1CIHBlciBQYXRpZW50IGNhc2UKV2Ugd2lsbCBleHBsb3JlIFRNQiBwZXIgYEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSURgIG92ZXIgdGltZSBieSBjcmVhdGluZyBzdGFja2VkIGJhcnBsb3RzLgoKYGBge3IgY3JlYXRlLXN0YWNrZWQtYmFycGxvdCwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA2LCBmaWcuZnVsbHdpZHRoID0gVFJVRX0KIyBEZWZpbmUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24KeWxpbSA9IG1heChkZiR0bWIpCgojIFJlLW9yZGVyIGRmCmYgPC0gYygiU2Vjb25kIE1hbGlnbmFuY3kiLCAiVW5hdmFpbGFibGUiLCAiRGVjZWFzZWQiLCAiUmVjdXJyZW5jZSIsICJQcm9ncmVzc2l2ZSIsICJEaWFnbm9zaXMiKSAjIExldmVsIGRmIGJ5IHRpbWVwb2ludHMKZGZfcGxvdCA8LSBkZiAlPiUgCiAgZHBseXI6OjptdXRhdGUodHVtb3JfZGVzY3JpcHRvciA9IGZhY3Rvcih0dW1vcl9kZXNjcmlwdG9yKSwKICAgICAgICAgICAgICAgICB0dW1vcl9kZXNjcmlwdG9yID0gZmN0X3JlbGV2ZWwodHVtb3JfZGVzY3JpcHRvciwgZikpIAoKIyBSdW4gZnVuY3Rpb24KZm5hbWUgPC0gcGFzdGUwKHBsb3RzX2RpciwgIi8iLCAiVE1CLWdlbm9taWMtdG90YWwucGRmIikKcHJpbnQoZm5hbWUpCnAgPC0gY3JlYXRlX3N0YWNrZWRfYmFycGxvdCh0bWJfZGYgPSBkZl9wbG90LCB5bGltID0geWxpbSkKcGRmKGZpbGUgPSBmbmFtZSwgd2lkdGggPSAxNSwgaGVpZ2h0ID0gNikKcHJpbnQocCkKZGV2Lm9mZigpCmBgYApBdHRlbnRpb246IEh5cGVybXV0YW50IFRNQiBkZWZpbmVkIGFzIOKJpTEwIE1iLCBhbmQgVWx0cmFoeXBlcm11dGFudCBUTUIgZGVmaW5lZCBhcyDiiaUxMDAgbXV0YXRpb25zL01iIGluIHBlZGlhdHJpYyBicmFpbiB0dW1vcnMgKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvMjkwNTYzNDQvKS4KCkhlcmUsIHdlIG5vdGljZSB0aGF0IHRoZXJlIGFyZSBzYW1wbGVzIHdpdGggaGlnaCBUTUIgKGh5cGVyLW11dGFudCBzYW1wbGVzKS4gTmV4dCwgd2Ugd2lsbCBleGNsdWRlIHRoZXNlIHNhbXBsZXMgKHRocmVzaG9sZCA+PSAxMCkgZnJvbSBkb3duc3RyZWFtIGFuYWx5c2lzLiBBdHRlbnRpb24gaXMgbmVlZGVkIGluIGNhc2VzIHdpdGggaGlnaCBudW1iZXIgb2YgbXV0YXRpb25zIGluIG9ubHkgb25lIHRpbWVwb2ludCBhcyB0aGlzIHdpbGwgbGVhZCB0byB1bi1tYXRjaGVkIGxvbmdpdHVkaW5hbCBzYW1wbGVzLiBXZSB3aWxsIGFsc28gcmVtb3ZlIHRob3NlIHNvIHdlIGFsd2F5cyBoYXZlIG1hdGNoZWQgbG9uZ2l0dWRpbmFsIHNhbXBsZXMuCgpgYGB7ciBjcmVhdGUtc3RhY2tlZC1iYXJwbG90LWZpbHRlciwgZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA4LCBmaWcuZnVsbHdpZHRoID0gVFJVRX0KIyBGaWx0ZXIgZGYgYW5kIHJlbW92ZSBhbnkgc2FtcGxlcyB3aXRoIHNpbmdsZSB0aW1lcG9pbnRzCmRmX3Bsb3RfZmlsdGVyIDwtIGRmICU+JQogIGZpbHRlcighdG1iID49IDEwKSAlPiUKICB1bmlxdWUoKSAlPiUgCiAgYXJyYW5nZShLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCB0dW1vcl9kZXNjcmlwdG9yKSAlPiUKICBncm91cF9ieShLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKHR1bW9yX2Rlc2NyaXB0b3Jfc3VtID0gc3RyX2ModHVtb3JfZGVzY3JpcHRvciwgY29sbGFwc2UgPSAiOyIpKSAlPiUgCiAgZmlsdGVyKCF0dW1vcl9kZXNjcmlwdG9yX3N1bSAlaW4lIGMoIkRpYWdub3NpcyIsICJQcm9ncmVzc2l2ZSIsICJSZWN1cnJlbmNlIiwgIlNlY29uZCBNYWxpZ25hbmN5IiwgIlVuYXZhaWxhYmxlIiwgIkRlY2Vhc2VkIiwgIlByb2dyZXNzaXZlO1Byb2dyZXNzaXZlIikpICU+JSAKICBkcGx5cjo6bGVmdF9qb2luKGRmLCBieSA9IGMoIktpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQiLCAidHVtb3JfZGVzY3JpcHRvcl9zdW0iKSkgJT4lIAogIG11dGF0ZSh0dW1vcl9kZXNjcmlwdG9yID0gZmFjdG9yKHR1bW9yX2Rlc2NyaXB0b3IpLAogICAgICAgICB0dW1vcl9kZXNjcmlwdG9yID0gZmN0X3JlbGV2ZWwodHVtb3JfZGVzY3JpcHRvciwgZikpICU+JSAKICBkcm9wX25hKHRtYikgCgojIGxlbmd0aCh1bmlxdWUoZGZfcGxvdF9maWx0ZXIkS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkpCgojIERlZmluZSBwYXJhbWV0ZXJzIGZvciBmdW5jdGlvbgp5bGltID0gbWF4KGRmX3Bsb3RfZmlsdGVyJHRtYikKZGZfcGxvdF9maWx0ZXIgPC0gZGZfcGxvdF9maWx0ZXIKCiMgUnVuIGZ1bmN0aW9uCmZuYW1lIDwtIHBhc3RlMChwbG90c19kaXIsICIvIiwgIlRNQi1nZW5vbWljLW5vLWh5cGVybXV0YW50cy5wZGYiKQpwcmludChmbmFtZSkKcCA8LSBjcmVhdGVfc3RhY2tlZF9iYXJwbG90KHRtYl9kZiA9IGRmX3Bsb3RfZmlsdGVyLCB5bGltID0geWxpbSkKcGRmKGZpbGUgPSBmbmFtZSwgd2lkdGggPSAyNSwgaGVpZ2h0ID0gOCkKcHJpbnQocCkKZGV2Lm9mZigpCmBgYAoKIyBUTUIgYWNyb3NzIHRpbWVwb2ludHMgYW5kIGNhbmNlciB0eXBlcyBwZXIgUGF0aWVudCBjYXNlCldlIHdpbGwgZXhwbG9yZSBUTUIgcGVyIGNhbmNlciBncm91cCBvdmVyIHRpbWUgYnkgY3JlYXRpbmcgZHVtYmJlbGwgcGxvdHMuIFdlIGNsYXNzaWZpZWQgYnkgdXNpbmcgY2FuY2VyIHR5cGVzIHdpdGggdGhlIGhpZ2hlc3QgbnVtYmVyIG9mIHNhbXBsZXMgKEhpZ2gtIGFuZCBMb3ctZ3JhZGUgZ2xpb21hcykgdmVyc3VzIGFueSBvdGhlciBjYW5jZXIgZ3JvdXBzLgoKYGBge3IgY3JlYXRlLWR1bWJiZWxsLWN0LCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDgsIGZpZy5mdWxsd2lkdGggPSBUUlVFfQojIEhvdyBtYW55IGJzX3NhbXBsZXMgcGVyIGtpZHNfaWQgYW5kIGNhbmNlciBncm91cD8KIyBwcmludCh0YWJsZShkZl9wbG90X2ZpbHRlciRjZ19wbG90KSkKcHJpbnQoZGZfcGxvdF9maWx0ZXIgJT4lIAogICAgICAgIGNvdW50KGNnX3Bsb3QsIEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpKQoKIyBEdW1iYmVsbCBwbG90IHBlciBjYW5jZXIgZ3JvdXAKY2FuY2VyX2dyb3VwcyA8LSB1bmlxdWUoYXMuY2hhcmFjdGVyKGRmX3Bsb3RfZmlsdGVyJGNnX3Bsb3QpKQpjYW5jZXJfZ3JvdXBzIDwtIHNvcnQoY2FuY2VyX2dyb3VwcywgZGVjcmVhc2luZyA9IEZBTFNFKQpwcmludChjYW5jZXJfZ3JvdXBzKQoKZm9yIChpIGluIHNlcV9hbG9uZyhjYW5jZXJfZ3JvdXBzKSkgewogIHByaW50KGkpCiAgZGZfY3Rfc3ViIDwtIGRmX3Bsb3RfZmlsdGVyICU+JSAKICAgIGZpbHRlcihjZ19wbG90ID09IGNhbmNlcl9ncm91cHMgW2ldKQogIAogICAgICBpZiAoaSAlaW4lIGMoMywgNywgOCkpIHsKICAgIHByaW50KGNhbmNlcl9ncm91cHMgW2ldKQogICAgIyBEZWZpbmUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24KICAgIHlsaW0gPC0gMgogICAgfSBlbHNlIGlmIChpID09IDIpIHsKICAgICAgcHJpbnQoY2FuY2VyX2dyb3VwcyBbaV0pCiAgICAgICMgRGVmaW5lIHBhcmFtZXRlcnMgZm9yIGZ1bmN0aW9uCiAgICAgIHlsaW0gPC0gNgogICAgICB9IGVsc2UgewogICAgICAgIHByaW50KGNhbmNlcl9ncm91cHMgW2ldKQogICAgICAgICMgRGVmaW5lIHBhcmFtZXRlcnMgZm9yIGZ1bmN0aW9uCiAgICAgICAgeWxpbSA8LSA0CiAgICAgIH0KICAgIAoKICAgICMgTmFtZSBwbG90cwogICAgZm5hbWUgPC0gcGFzdGUwKHBsb3RzX2RpciwgIi8iLCBjYW5jZXJfZ3JvdXBzW2ldLCAiLVRNQi1kdW1iYmVsbCIsICIucGRmIikKICAgIHByaW50KGZuYW1lKQogICAgCiAgICAjIFJ1biBmdW5jdGlvbgogICAgcCA8LSBjcmVhdGVfZHVtYmJlbGxfY3QodG1iX2RmID0gZGZfY3Rfc3ViLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWxpbSA9IHlsaW0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdF9pZCA9IGNhbmNlcl9ncm91cHNbaV0pCiAgICBwZGYoZmlsZSA9IGZuYW1lLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA4KQogICAgcHJpbnQocCkKICAgIGRldi5vZmYoKQp9CmBgYAoKIyBUb3RhbCBudW1iZXIgb2YgbXV0YXRpb25zIGFjcm9zcyB0aW1lcG9pbnRzIGFuZCBiaW9zcGVjaW1lbiBzYW1wbGUgcGVyIFBhdGllbnQgY2FzZQpIZXJlLCB3ZSB3YW50IHRvIGV4cGxvcmUgdGhlIG51bWJlciBvZiBtdXRhdGlvbnMgcGVyIHRpbWVwb2ludCBhbmQgYmlvc3BlY2ltZW4gc2FtcGxlIHBlciBwYXRpZW50IGNhc2UuCgpgYGB7ciBjcmVhdGUtYmFycGxvdC1zYW1wbGUsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA0LCBmaWcuZnVsbHdpZHRoID0gVFJVRX0Kc2FtcGxlcyA8LSB1bmlxdWUoYXMuY2hhcmFjdGVyKGRmX3Bsb3RfZmlsdGVyJEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpKQpwcmludChzYW1wbGVzKQoKZm9yIChpIGluIHNlcV9hbG9uZyhzYW1wbGVzKSkgewogIHByaW50KGkpCiAgdG1iX3N1YiA8LSBkZl9wbG90X2ZpbHRlciAlPiUKICAgIGZpbHRlcihLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEID09IHNhbXBsZXNbaV0pCiAgCiAgIyBEZWZpbmUgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24KICB5bGltID0gbWF4KGRmX3Bsb3RfZmlsdGVyJG11dGF0aW9uX2NvdW50KQogCiAgIyBSdW4gZnVuY3Rpb24KICBwIDwtIGNyZWF0ZV9iYXJwbG90X3NhbXBsZSh0bWJfZGYgPSB0bWJfc3ViLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsaW0gPSB5bGltLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZCA9IHNhbXBsZXNbaV0pCiAgcHJpbnQocCkKfQpgYGAKCmBgYHtyIGVjaG89VFJVRX0Kc2Vzc2lvbkluZm8oKQpgYGAK